package songgao.papers.iEC;

import java.util.List;

import javax.jdo.JDOHelper;
import javax.jdo.PersistenceManager;
import javax.jdo.PersistenceManagerFactory;
import javax.jdo.Query;
import javax.jdo.Transaction;

import com.sun.org.apache.xalan.internal.xsltc.runtime.Parameter;

import java.util.Collections;
import net.sf.jsr107cache.Cache;
import net.sf.jsr107cache.CacheException;
import net.sf.jsr107cache.CacheFactory;
import net.sf.jsr107cache.CacheManager;

public enum DataManager {
	INSTANCE;
	public static class PMF {
		private static final PersistenceManagerFactory pmfInstance = JDOHelper.getPersistenceManagerFactory("transactions-optional");

		private PMF() {
		}

		public static PersistenceManagerFactory get() {
			return pmfInstance;
		}
	}
	
	private java.util.Random randomizer;
	
	public synchronized void ClearAll(){
		PersistenceManager pm = PMF.get().getPersistenceManager();
		pm.newQuery(Master.class).deletePersistentAll();
		pm.newQuery(Parameters.class).deletePersistentAll();
		pm.newQuery(Individual.class).deletePersistentAll();
		pm.newQuery(Population.class).deletePersistentAll();
		pm.newQuery(Slave.class).deletePersistentAll();
		
		this.randomizer=null;
		
		this.getMaster();
	}

	/**
	 * @author songgao
	 * 
	 * @param <T>
	 *            The Type of object that this default getter returns.
	 */
	private interface DefaultGetable<T> {
		T getDefault();
	}

	/**
	 * @param <T>
	 *            The Type of stored data that will be retrieved.
	 * @param type
	 *            Class of T. This is used for JDO.
	 * @param defaultGetter
	 *            A getter that returns a default object. This is used when there's no object of this type is stored in database and a default one need to be created.
	 * @return The only object of this type stored in the database.
	 */
	@SuppressWarnings("unchecked")
	private synchronized <T> T getSingletonData(Class<T> type, DefaultGetable<T> defaultGetter) {
		T r;
		PersistenceManager pm = PMF.get().getPersistenceManager();
		pm.setDetachAllOnCommit(true);
		pm.getFetchPlan().setMaxFetchDepth(-1);
		Transaction tx = pm.currentTransaction();
		Query query = pm.newQuery(type);
		List<T> result = (List<T>) query.execute();
		if (result.size() == 0) {
			if (defaultGetter != null){
				r=defaultGetter.getDefault();
				tx.begin();
				pm.makePersistent(r);
				tx.commit();
			}
			else
				return null;
		} else {
			while (result.size() > 1)
				pm.deletePersistent(result.get(1));
			r = pm.detachCopy(result.get(0));
		}
		pm.close();
		return r;
	}

	public synchronized Boolean setParameters(final Parameters p) {

		if (getSingletonData(Parameters.class, null) != null) {
			return false;
		} else {
			final DefaultGetable<Parameters> defaultGetter = new DefaultGetable<Parameters>() {
				@Override
				public Parameters getDefault() {
					return p;
				}
			};
			this.getSingletonData(Parameters.class, defaultGetter);
			
			return true;
		}
	}

	public synchronized Parameters getParameters() {
		
		final DefaultGetable<Parameters> defaultGetter = new DefaultGetable<Parameters>() {
			@Override
			public Parameters getDefault() {
				return Parameters.generateDefault();
			}
		};
		Parameters parameters = this.getSingletonData(Parameters.class, defaultGetter);
		return parameters;
	}

	public synchronized Master getMaster() {

		final DefaultGetable<Master> defaultGetter = new DefaultGetable<Master>() {
			@Override
			public Master getDefault() {
				return Master.generateMaster();
			}
		};
		Master master = this.getSingletonData(Master.class, defaultGetter);
		return master;
	}

	public synchronized Boolean modifiedParameters(Parameters parameters) {
		PersistenceManager pm = PMF.get().getPersistenceManager();
		pm.setDetachAllOnCommit(true);
		pm.getFetchPlan().setMaxFetchDepth(-1);
		Transaction tx = pm.currentTransaction();
		try {
			tx.begin();
			pm.makePersistent(parameters);
			tx.commit();
			pm.close();
			return true;
		} catch (Exception ex) {
			return false;
		}
	}

	public synchronized Boolean modifiedMaster(Master master) {
		PersistenceManager pm = PMF.get().getPersistenceManager();
		pm.setDetachAllOnCommit(true);
		pm.getFetchPlan().setMaxFetchDepth(-1);
		Transaction tx = pm.currentTransaction();
		try {
			tx.begin();
			pm.makePersistent(master);
			tx.commit();
			pm.close();
			return true;
		} catch (Exception ex) {
			return false;
		}
	}

	public synchronized String newIndividual(Individual individual) {
		PersistenceManager pm = PMF.get().getPersistenceManager();
		String key = pm.makePersistent(individual).getEncodedKey();
		pm.close();
		return key;
	}

	public synchronized Individual getIndividual(String individualKey) {
		PersistenceManager pm = PMF.get().getPersistenceManager();
		Individual r = pm.getObjectById(Individual.class, individualKey);
		pm.close();
		return r;
	}

	public synchronized boolean modifiedIndividual(Individual individual) {
		PersistenceManager pm = PMF.get().getPersistenceManager();
		try {
			pm.makePersistent(individual);
			pm.close();
			return true;
		} catch (Exception ex) {
			pm.close();
			return false;
		}
	}

	/**
	 * @return the randomizer
	 */
	public synchronized java.util.Random getRandomizer() {
		if (this.randomizer == null)
			this.randomizer = new java.util.Random();
		return randomizer;
	}
}
